class Print {
    standards = {
        strict: 'strict',
        loose: 'loose',
        html5: 'html5'
    }
    elsdom: Nullable<HTMLElement> = null
    counter = 0
    settings: any = {
        standard: this.standards.html5,
        extraHead: '', // 附加在head标签上的额外元素,使用逗号分隔
        extraCss: '', // 额外的css逗号分隔
        popTitle: '', // 标题
        endCallback: null, // 成功打开后的回调函数
        id: '',
        ids: '' // 局部打印的id
    }
    constructor(option: any) {
        Object.assign(this.settings, option)
        this.init()
    }
    init() {
        this.counter++
        this.settings.id = `printArea_${this.counter}`
        const PrintAreaWindow = this.getPrintWindow() // 创建iframe
        this.write(PrintAreaWindow.doc) // 写入内容
        this.print(PrintAreaWindow)
        this.settings.endCallback()
    }
    print(PAWindow: any) {
        const paWindow = PAWindow.win
        const _loaded = () => {
            paWindow.focus()
            paWindow.print()
            try {
                const box = document.getElementById(this.settings.id)
                const canvasList = this.elsdom ? this.elsdom.querySelectorAll('.canvasImg') : []
                for (let i = 0; i < canvasList.length; i++) {
                    const _parent = canvasList[i].parentNode
                    _parent && _parent.removeChild(canvasList[i])
                }
                if (box && box.parentNode) {
                    box.parentNode.removeChild(box)
                }
            } catch (e) {
                console.log(e)
            }
        }
        if (window.ActiveXObject) {
            paWindow.onload = _loaded()
        } else {
            paWindow.onload = () => {
                _loaded()
            }
        }
    }
    write(PADocument: any) {
        PADocument.open()
        PADocument.write(`${this.docType()}<html>${this.getHead()}${this.getBody()}</html>`)
        PADocument.close()
    }
    docType() {
        if (this.settings.standard === this.standards.html5) {
            return '<!DOCTYPE html>'
        }
        const transitional = this.settings.standard === this.standards.loose ? ' Transitional' : ''
        const dtd = this.settings.standard === this.standards.loose ? 'loose' : 'strict'

        return `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01${transitional}//EN" "http://www.w3.org/TR/html4/${dtd}.dtd">`
    }
    getHead() {
        let extraHead = ''
        let links = ''
        let style = ''
        if (this.settings.extraHead) {
            this.settings.extraHead.replace(/([^,]+)/g, (m: string) => {
                extraHead += m
            })
        }
        // 复制所有link标签
        ;[].forEach.call(document.querySelectorAll('link'), function (item: HTMLLinkElement) {
            if (item.href.indexOf('.css') >= 0) {
                links += `<link type="text/css" rel="stylesheet" href="${item.href}" >`
            }
        })

        // 循环获取style标签的样式
        const domStyle = document.styleSheets
        if (domStyle && domStyle.length > 0) {
            for (let i = 0; i < domStyle.length; i++) {
                try {
                    if (domStyle[i].cssRules || domStyle[i].rules) {
                        const rules = domStyle[i].cssRules || domStyle[i].rules
                        for (let b = 0; b < rules.length; b++) {
                            style += rules[b].cssText
                        }
                    }
                } catch (e) {
                    console.log(domStyle[i].href + e)
                }
            }
        }

        if (this.settings.extraCss) {
            this.settings.extraCss.replace(/([^,\s]+)/g, (m: string) => {
                links += `<link type="text/css" rel="stylesheet" href="${m}">`
            })
        }

        return `<head><title>${this.settings.popTitle}</title>${extraHead}${links}<style type="text/css">${style}</style></head>`
    }
    getBody() {
        let ids = this.settings.ids
        ids = ids.replace(new RegExp('#', 'g'), '')
        this.elsdom = this.beforeHanler(document.getElementById(ids))
        const ele = this.getFormData(this.elsdom)
        const htm = ele.outerHTML
        return '<body>' + htm + '</body>'
    }
    // 克隆节点之前做的操作
    beforeHanler(elsdom: Nullable<HTMLElement>) {
        const canvasList = elsdom ? elsdom.querySelectorAll('canvas') : []
        // canvas转换png图片
        for (let i = 0; i < canvasList.length; i++) {
            if (!canvasList[i].style.display) {
                const _parent = canvasList[i].parentNode
                const _canvasUrl = canvasList[i].toDataURL('image/png')
                const _img = new Image()
                _img.className = 'canvasImg'
                _img.style.display = 'none'
                _img.src = _canvasUrl
                _parent!.appendChild(_img)
            }
        }
        return elsdom
    }
    // 根据type去处理form表单
    getFormData(ele: any) {
        const copy = ele.cloneNode(true)
        const copiedInputs = copy.querySelectorAll('input,select,textarea')
        const canvasImgList = copy.querySelectorAll('.canvasImg,canvas')
        let selectCount = -1
        // 处理所有canvas
        for (let i = 0; i < canvasImgList.length; i++) {
            const _parent = canvasImgList[i].parentNode
            const item = canvasImgList[i]
            // 删除克隆后的canvas节点
            if (item.tagName.toLowerCase() === 'canvas') {
                _parent.removeChild(item)
            } else {
                item.style.display = 'block'
            }
        }
        // 处理所有输入框
        for (let i = 0; i < copiedInputs.length; i++) {
            const item = copiedInputs[i]
            let typeInput = item.getAttribute('type')
            const copiedInput = copiedInputs[i]
            // 获取select标签
            if (!typeInput) {
                typeInput = item.tagName === 'SELECT' ? 'select' : item.tagName === 'TEXTAREA' ? 'textarea' : ''
            }
            // 处理input框
            if (item.tagName === 'INPUT') {
                // 除了单选框 多选框比较特别
                if (typeInput === 'radio' || typeInput === 'checkbox') {
                    copiedInput.setAttribute('checked', item.checked)
                    //
                } else {
                    copiedInput.value = item.value
                    copiedInput.setAttribute('value', item.value)
                }
                // 处理select
            } else if (typeInput === 'select') {
                selectCount++
                for (let b = 0; b < ele.querySelectorAll('select').length; b++) {
                    const select = ele.querySelectorAll('select')[b] // 获取原始层每一个select
                    !select.getAttribute('newbs') && select.setAttribute('newbs', b) // 添加标识
                    if (select.getAttribute('newbs') == selectCount) {
                        const opSelectedIndex = ele.querySelectorAll('select')[selectCount].selectedIndex
                        item.options[opSelectedIndex].setAttribute('selected', true)
                    }
                }
                // 处理textarea
            } else {
                copiedInput.innerHTML = item.value
                copiedInput.setAttribute('html', item.value)
            }
        }

        return copy
    }
    getPrintWindow() {
        const f = this.Iframe()
        return {
            f: f,
            win: f.contentWindow || f,
            doc: f.doc
        }
    }
    Iframe() {
        const frameId = this.settings.id
        let iframe: any
        try {
            iframe = document.createElement('iframe')
            document.body.appendChild(iframe)
            iframe.style.border = '0px'
            iframe.style.position = 'absolute'
            iframe.style.width = '0px'
            iframe.style.height = '0px'
            iframe.style.right = '0px'
            iframe.style.top = '0px'
            iframe.setAttribute('id', frameId)
            iframe.setAttribute('src', new Date().getTime())
            iframe.doc = null
            iframe.doc = iframe.contentDocument ? iframe.contentDocument : iframe.contentWindow ? iframe.contentWindow.document : iframe.document
            iframe.onload = () => {
                const win = iframe.contentWindow || iframe
                this.print(win)
            }
        } catch (e) {
            throw new Error(e + '. iframes may not be supported in this browser.')
        }

        if (iframe.doc == null) {
            throw new Error('Cannot find document.')
        }

        return iframe
    }
}
export default Print
